前言OpenSSL 命令行开始确定OpenSSL版本和配置构建OpenSSL检查可用命令构建信任存储手动转换密钥和证书管理密钥生成创建证书签名请求从现有证书创建CSR无人值守CSR生成签署您自己的证书创建对多个主机名有效的证书检查证书检查公共证书密钥和证书转换PEM和DER转换PKCS #12(PFX)转换PKCS #7 转换配置获取支持的套件了解安全级别配置TLS 1.3配置OpenSSL默认值建议的套件配置生成DH参数旧套件配置关键字组合关键字构建密码套件列表关键字编辑器排序处理错误性能创建私有证书颁发机构功能和限制创建根CA根CA配置根CA目录结构根CA生成数据库文件的结构根CA操作创建OCSP签名证书创建下级CA下级CA配置下级CA生成下属CA操作使用OpenSSL测试TLS自定义编译OpenSSL以进行测试连接到TLS服务证书验证测试升级到TLS的协议提取远程证书测试协议支持测试密码套件配置测试密码套件首选项测试命名组测试DANE测试会话恢复在连接之间保持会话状态检查OCSP吊销测试OCSP装订检查CRL吊销测试重新协商检测心脏出血确定Diffie-Hellman参数的强度

前言

尽管有缺点,OpenSSL是最成功、最重要的开源项目之一。它之所以成功,是因为它被广泛使用;它很重要,因为互联网基础设施的大部分安全都依赖于它。该项目包括密钥加密算法的高性能实现、完整的TLS和PKI堆栈以及命令行工具包。我认为可以肯定地说,如果你的工作与安全、web开发或系统管理有关,那么至少在某种程度上,你不可避免地要处理OpenSSL。互联网的大部分由开源产品提供支持,其中大多数依赖于OpenSSL。

本书介绍了使用OpenSSL的两种方法:

这两章都是从我的大型作品《Bulletproof TLS and PKI》中借来的。我决定将OpenSSL章节作为单独的免费书籍出版,因为严重缺乏良好且易于获取的文档。对于复杂和长期存在的项目来说,你在互联网上找到的OpenSSL文档往往是错误和过时的。

此外,出版商经常放弃一个或多个章节来展示这本书的样子,我认为我应该充分利用这种做法,不仅要让OpenSSL章节免费,还要承诺随着时间的推移继续维护和改进它们。所以他们来了。

OpenSSL 命令行

OpenSSL是世界上使用最广泛的传输层安全(Transport Layer Security,TLS)协议的实现。在核心上,它也是一个健壮的、高性能的加密库,支持广泛的加密原语(primitives)。除了库代码之外,OpenSSL还提供了一组用于各种用途的命令行工具,包括对常见PKI操作和TLS测试的支持。

OpenSSL是这一领域事实上的标准,具有悠久的历史。该代码最初于1995年以SSLeay的名字开始使用(SSLeay名字中的字母“eay”是Eric A.Young的缩写。),当时由Eric A.Young和Tim J.Hudson开发。OpenSSL作为一个单独的项目诞生于1998年,当时Eric和Tim决定开始开发一个名为BSAFE SSL-C的商业SSL/TLS工具包。开发人员社区拿起了该项目,并继续维护它。

今天,OpenSSL在服务器端和许多客户端程序中无处不在。命令行工具也是密钥和证书管理的最常见选择。就浏览器而言,OpenSSL也有很大的市场份额,尽管是通过谷歌的分支,称为 BoringSSL

OpenSSL过去在OpenSSL和SSLeay许可证下是双重许可的。两者都类似BSD,有广告条款。在2021年9月发布的3.0版本中,OpenSSL通过迁移到Apache License v2.0简化了其许可。

开始

如果您使用的是Unix平台之一,那么开始使用OpenSSL应该很容易;实际上,您可以保证已经在系统上安装了它。然而,事情可能会出差错。例如,您可能有一个不正确的版本,或者可能有其他工具(例如,LibreSSL)配置为在调用OpenSSL时响应。由于这个原因,最好首先检查您已经安装了什么,并且只有在绝对必要时才使用自定义安装。另一种选择是寻找打包平台。例如,对于OS X,您可以使用Brew或MacPorts。一如既往,从头开始编译东西很少是问题;无限期地维护该软件才是。

在本章中,我假设您使用的是Unix平台,因为这是OpenSSL的自然环境。在Windows上,从头编译软件不太常见,因为工具不容易获得。您仍然可以自己编译OpenSSL,但可能需要更多的工作。或者,您可以考虑从Shining Light Productions网站下载二进制文件。如果您从多个网站下载二进制文件,则需要确保它们不是在不同版本的OpenSSL下编译的。如果是,您可能会遇到难以排除故障的崩溃。最好的方法是使用单个程序包,其中包含您需要的所有内容。例如,如果要在Windows上运行Apache,可以从Apache Lounge网站获取二进制文件。

确定OpenSSL版本和配置

在你做任何工作之前,你应该知道你将使用哪个OpenSSL版本。TLS和PKI继续以相当快的速度发展,如果你的OpenSSL版本不支持它们,你可能会发现你能做的事情是有限的。以下是我在Ubuntu 20.04 LTS上使用openssl版本获得的版本信息,这是我将在本章示例中使用的系统:

在撰写本文时,OpenSSL 1.1.1是生产中使用的主要分支,具有所有优秀的功能。在较旧的系统上,您可能会发现1.1.0分支的版本,这很好,因为它可以与TLS 1.2安全地使用,但它不支持TLS 1.3等现代功能。另一个方向是OpenSSL 3.0,它引入了库的重大更新,进行了实质性的架构更改,并切换到Apache License 2.0,以更好地与其他程序和库互操作。命令行工具,即我在本章和下一章中介绍的工具,应该基本相同。也就是说,每个版本,尤其是主要版本,都很可能以微妙的方式改变工具的行为。当您从一个分支更改到另一个分支时,有必要查看更改文档以了解可能存在的差异。

要获取完整的版本信息,请使用 -a 开关:

我想你最初不会觉得这个输出很有趣,但知道在哪里可以找到你的OpenSSL是如何编译的是很有用的。特别有趣的是 OPENSSLDIR 设置,在我的例子中,它指向 /usr/lib/ssl ;它将告诉您OpenSSL在哪里查找其默认配置和根证书。在我的系统上,该位置基本上是 /etc/ssl 的别名,这是Ubuntu PKI相关文件的主要位置:

misc/ 文件夹包含一些补充脚本,其中最有趣的是允许您实现私有证书颁发机构(CA)的脚本。你可能会也可能不会最终使用它,但在本章的后面,我将向你展示如何从头开始做同样的工作。

构建OpenSSL

在大多数情况下,您将使用系统提供的OpenSSL版本,但有时有充分的理由使用较新或较旧的版本。例如,如果您有一个较旧的系统,它可能会使用不支持TLS 1.3的OpenSSL版本。另一方面,较新的OpenSSL版本可能不支持SSL 2或SSL 3。虽然这在一般情况下是正确的做法,但如果你的工作是测试系统的安全性,你需要支持这些旧功能。

您可以从下载最新版本的OpenSSL(在我的例子中是1.1.1g)开始:

下一步是在编译之前配置OpenSSL。为此,您通常会使用配置脚本,该脚本首先尝试猜测您的架构,然后运行整个配置过程:

自动架构检测有时可能会失败(例如,在OS X上使用旧版本的OpenSSL),在这种情况下,您应该使用显式架构字符串调用Configure脚本。配置语法在其他方面是相同的。

除非您确定不想这样做,否则必须使用 --prefix 选项将OpenSSL安装到与系统提供的版本不冲突的私有位置。如果出错,可能会损坏您的服务器。另一个重要的选择是不共享,它强制静态链接并制作自包含的命令行工具。如果不使用此选项,则需要使用 LD_LIBRARY_PATH 配置才能使工具工作。

编译OpenSSL 1.1.0或更高版本时, OPENSSL_TLS_SECURITY_LEVEL 选项配置默认安全级别,该级别为所有库用户建立默认的最低安全要求。在编译时设置此值非常有用,因为它可用于防止配置错误。我将在本章稍后更详细地讨论安全级别。

enable-ec_nistp_64_gcc_128 参数激活某些常用椭圆曲线的优化版本。这种优化依赖于无法自动检测到的编译器功能,这就是为什么默认情况下它被禁用的原因。OpenSSL wiki上提供了完整的配置选项集。

如果你正在编译1.1.0之前的版本,你需要先构建依赖关系:

OpenSSL 1.1.0及以上版本将自动执行此操作,因此您可以使用以下内容继续构建主包:

您将在 /opt/openssl 中获得以下内容:

private/ 文件夹为空,但这是正常的;您还没有任何私钥。另一方面,您可能会惊讶地发现 certs/ 文件夹也是空的。OpenSSL不包含任何根证书;维护信任存储被认为不在项目范围内。幸运的是,您的操作系统可能已经附带了可以立即使用的信任存储。以下内容在我的服务器上运行:

检查可用命令

OpenSSL是一个加密工具包,由许多不同的实用程序组成。我在我的版本中数了48。如果有一个合适的时机使用密码学中的“瑞士军刀”一词,那就是现在。即使你只会使用少数实用程序,你也应该熟悉所有可用的工具,因为你永远不知道未来可能需要什么。

要了解所提供的内容,只需请求帮助:

help 输出的第一部分列出了所有可用的实用程序。要获取有关特定实用程序的更多信息,请使用man命令,后跟实用程序的名称。例如,man ciphers 将为您提供有关密码套件配置方式的详细信息。然而, man openssl-ciphers 也应该能用:

帮助输出实际上并没有到此为止,但其余的部分就没那么有趣了。在第二部分中,您将获得消息摘要(message digest)命令列表:

然后在第三部分中,您将看到所有密码(cipher)命令的列表:

构建信任存储

OpenSSL不附带受信任的根证书集合(也称为根存储——root store 或信任存储——trust store ),因此如果您从头开始安装,则必须在其他地方找到它们。一种可能是使用操作系统中内置的信任存储,正如我之前所示。这种选择通常很好,但内置的信任存储可能并不总是最新的。此外,在混合环境中,各种系统中的默认存储之间可能存在有意义的差异。一个一致且可能更好的选择——但需要更多的工作——是重用(reuse)Mozilla的工作。Mozilla投入了大量精力来维护一个透明且最新的根存储,以供Firefox使用。

因为它是开源的,Mozilla将信任存储保存在源代码存储库中:

https://hg.mozilla.org/releases/mozilla-beta/file/tip/security/nss/lib/ckfw/builtins/certdata.txt

不幸的是,它的证书集合是专有格式的,对其他人来说并没有多大用处。如果你不介意通过第三方获取集合,Curl项目提供了一个定期更新的隐私增强邮件(Privacy-Enhanced Mail,PEM)格式的转换,你可以直接使用:

http://curl.haxx.se/docs/caextract.html

如果你更愿意直接与Mozilla合作,你可以使用Curl项目使用的相同工具转换它的数据。您将在下一节中找到有关它的更多信息。

此时,您拥有的是一个根存储,其中所有受信任的证书都在同一个文件中。如果你只打算将其与 s_client 工具一起使用,这将很好地工作。在这种情况下,您需要做的就是将 -CAfile 开关指向您的根存储。更换服务器上的根存储将需要更多的工作,具体取决于使用的操作系统。

例如,在Ubuntu上,您需要替换 /etc/ssl/certs 文件夹的内容。Ubuntu附带了一个名为 update-ca-certificates 的工具,该工具可能有效。或者,您可以通过复制现有数据的结构手动进行更改。从外观上看,该文件夹包含作为单独文件的受信任证书,以及所有这些证书在一个名为 ca-certificates.crt 的文件中。您还将观察到一些符号链接;它们是由OpenSSL的 rehashc_rehash 工具创建的。任何手动更改的缺点是,当系统更新时,它们可能会被覆盖。

手动转换

为了转换Mozilla的根存储,Curl项目使用了最初由Guenter Knauf编写的Perl脚本。此脚本是Curl项目的一部分,但您可以通过以下链接直接下载:

https://raw.githubusercontent.com/curl/curl/master/lib/mk-ca-bundle.pl

下载并运行脚本后,它将从Mozilla获取证书数据并将其转换为PEM格式:

如果保留以前下载的证书数据,脚本将使用它来确定更改的内容,并仅处理更新。

密钥和证书管理

大多数用户转向OpenSSL,因为他们希望配置和运行支持SSL的web服务器。该过程包括三个步骤:

  1. 生成私钥(generate a private key)
  2. 创建证书签名请求(Certificate Signing Request,CSR)并将其发送到CA
  3. 在您的web服务器中安装CA提供的证书。

本节将介绍这些步骤(以及其他几个步骤)。

密钥生成

准备运行TLS服务器的第一步是生成私钥。在开始之前,您必须做出几个决定:

要生成RSA密钥,请使用以下 genpkey 命令:

在这里,我指定使用AES-128保护密钥。您也可以使用AES-256(使用 -aes-256-cbc 开关),但最好远离其他算法(例如 DES、3DES和SEED)。

当您使用 genpkey 命令时,生成的私钥以PKCS#8格式存储,该格式只是文本,看起来不太像:

然而,私钥不仅仅是一团随机数据,即使它看起来是这样的。您可以使用以下 rsa 命令查看密钥的结构:

如果你只需要单独拥有密钥的公共部分,你可以使用以下rsa命令:

如果你查看新生成的文件,你会看到标记清楚地表明所包含的信息确实是公开的:

验证输出是否包含您所期望的内容是很好的做法。例如,如果您忘记在命令行中包含 -pubout 开关,则输出将包含您的私钥而不是公钥。

该过程与ECDSA密钥类似,只是不可能创建任意大小的密钥。相反,对于每个密钥,您可以选择一条命名曲线(named curve),该曲线控制密钥点大小,但也控制其他EC参数。以下示例使用P-256(或secp256r1)命名的曲线创建256位ECDSA密钥:

OpenSSL支持许多命名曲线,但对于web服务器密钥,通常(仍然)仅限于广泛支持的两条曲线:P-256(也称为secp256r1或prime256v1)和P-384(secp384r1)。在这两者中,P-256足够安全,性能更好。如果您想查看OpenSSL支持的所有命名曲线的列表,可以使用 ecparam 命令和 -list_curves 开关来获取。

也支持最近添加的x25519、x448、ed25519和ed448,但它们是不同类型的曲线,必须使用 -algorithm 开关指定,例如:

创建证书签名请求

一旦你有了私钥,你就可以继续创建证书签名请求(Certificate Signing Request,CSR)。这是一个要求CA签署证书的正式请求,它包含请求证书的实体的公钥和有关该实体的一些信息。这些数据都将成为证书的一部分。CSR总是使用与其携带的公钥相对应的私钥进行签名。

CSR创建通常是一个交互式过程,在此过程中,您将提供证书可分辨名称的元素。仔细阅读openssl工具给出的说明;如果你想让一个字段为空,你必须在行上输入一个点(.),而不是直接点击 Return 。如果执行后者,OpenSSL将用默认值填充相应的CSR字段。(当与默认的OpenSSL配置一起使用时,这种行为没有任何意义,这几乎是每个人都会做的。一旦你意识到你实际上可以通过修改OpenSSL配置或提供自己的配置文件来更改默认值,这种行为就有意义了。)

生成CSR后,使用它来签署您自己的证书和/或将其发送到公共CA并要求其签署证书。以下章节将介绍这两种方法。但在你这样做之前,最好仔细检查CSR是否正确。方法如下:

从现有证书创建CSR

如果您要续订证书并且不想对其中显示的信息进行任何更改,则可以节省一些打字时间。使用以下命令,您可以从现有证书创建一个全新的CSR:

无人值守CSR生成

CSR生成不一定是交互式的。使用自定义OpenSSL配置文件,您既可以自动执行该过程(如本节所述),也可以执行某些无法交互的操作(例如,如何在同一证书中包含多个域名,如后续章节所述)。

例如,假设我们想为www.feistyduck.com自动生成CSR。我们将首先创建一个包含以下内容的文件 fd.cnf

现在,您可以直接从命令行创建CSR:

签署您自己的证书

如果你正在配置TLS服务器供自己使用或进行快速测试,有时你不想去CA获取公开可信的证书。使用自签名证书要容易得多。

如果您已经拥有CSR,请使用以下命令创建证书:

实际上,您不必在单独的步骤中创建CSR。以下命令创建仅以密钥开头的自签名证书:

如果您不希望被问到任何问题,请使用 -subj 开关在命令行上提供证书使用者信息:

创建对多个主机名有效的证书

默认情况下,OpenSSL生成的证书只有一个通用名称,并且只对一个主机名有效。因此,即使您有相关的网站,您也必须为每个网站使用单独的证书。在这种情况下,使用单个多域证书更有意义。此外,即使您运行的是单个网站,您也需要确保证书对最终用户访问它的所有可能路径都有效。在实践中,这意味着至少使用两个名称,一个带有www前缀,一个没有(例如www.feistyduck.com和feistydack.com)。

有两种机制可以在证书中支持多个主机名。第一种方法是使用名为主题备选名称(SAN)的X.509扩展列出所有所需的主机名。第二种是使用通配符。如果更方便,您也可以将这两种方法结合使用。在实践中,对于大多数网站,您可以指定一个裸域名和一个通配符来覆盖所有子域(例如,feistyduck.com和*.feisyduck.com)。

首先,将扩展信息放在单独的文本文件中。我将其命名为 fd.ext 。在文件中,指定扩展名(subjectAltName)的名称,并列出所需的主机名,如下例所示:

然后,当使用x509命令颁发证书时,请使用 -extfile 开关参考该文件:

剩下的过程与以前没有什么不同。但是,当您稍后检查生成的证书时(请参阅下一节),您会发现它包含SAN扩展:

【《TLS Mastery》第三章中明确反对使用通配符证书,风险来自多台主机更容易面临私钥丢失的可能。】

检查证书

证书在文本编辑器中看起来不太像,但它们包含大量信息;您只需要知道如何解压缩它。x509命令就可以做到这一点,所以让我们用它来查看您生成的自签名证书。

在以下示例中,我使用 -text 开关打印证书内容,使用 -noout 通过不打印编码的证书本身来减少混乱(这是默认行为):

自签名证书通常只包含最基本的证书数据,其中大部分是不言自明的。本质上,证书的主体是添加签名的。相比之下,公共CA颁发的证书更有趣,因为它们包含许多附加字段(通过X.509扩展机制)。

检查公共证书

当您查看公共证书时,在输出的第一部分中,您将找到与自签名证书中类似的信息。事实上,唯一的区别是,如颁发者信息所示,此证书具有不同的父级。

主要区别在于X.509扩展,它包含了大量非常有趣的信息。让我们检查一下扩展中有什么,以及为什么它在那里,没有特别的顺序。

基本约束Basic Constraints)扩展用于将证书标记为属于CA,使其能够签署其他证书。非CA证书将省略此扩展名,或者将CA的值设置为 FALSE 。此扩展至关重要,这意味着所有使用证书的软件都必须理解其含义。

密钥使用Key Usage,KU)和 扩展密钥使用Extended Key Usage,EKU)扩展限制了证书的用途。如果存在这些扩展,则只允许使用列出的用途。如果扩展不存在,则没有使用限制。您在这个例子中看到的是典型的web服务器证书,例如,它不允许代码签名:

CRL分发点CRL Distribution Points)扩展列出了可以找到CA证书吊销列表Certificate Revocation List,CRL)信息的地址。在需要吊销证书的情况下,此信息很重要。CRL是CA签名的吊销证书列表,按固定时间间隔(例如七天)发布。Let's Encrypt不提供CRL,因此我从另一个证书中获取了以下代码片段:

证书策略Certificate Policies)扩展用于指示颁发证书所依据的策略。例如,您可以在这里找到用于确定所有者身份的验证类型的指示。可以找到扩展验证Extended Validation,EV)指标(如下例所示)。这些指示器采用唯一对象标识符(Object Identifiers,OIDs)的形式,其中一些是通用的,一些是特定于颁发CA的。在以下示例中, OID 2.23.140.1.2.1 表示域验证的证书。此外,此扩展通常包含一个或多个 证书策略声明Certificate Policy Statement ,CPS)点,这些点通常是网页或PDF文档。

权威信息访问Authority Information Access,AIA)扩展通常包含两条重要信息。首先,它列出了CA的在线证书状态协议Online Certificate Status Protocol,OCSP)响应者的地址,可用于实时检查证书吊销情况。扩展还可能包含一个链接,指向可以找到颁发者证书(链中的下一个证书)的位置。如今,服务器证书很少由受信任的根证书直接签名,这意味着用户必须在其配置中包含一个或多个中间证书。错误很容易犯,会使证书失效。一些客户端将使用此扩展中提供的信息来修复不完整的证书链,但许多客户端不会。

主题密钥标识符Subject Key Identifier)和权威密钥标识符Authority Key Identifier)扩展分别建立唯一的主题和权威密钥标识。证书的授权密钥标识符扩展中指定的值必须与颁发证书中的主题密钥标识符扩展指定的值匹配。此信息在证书路径构建过程中非常有用,在该过程中,客户端试图找到从叶子(服务器)证书到受信任根的所有可能路径。证书颁发机构通常会使用一个私钥和多个证书,此字段允许软件可靠地识别哪个证书可以与哪个密钥匹配。在现实世界中,服务器提供的许多证书链都是无效的,但这一事实往往被忽视,因为浏览器能够找到替代的信任路径。

主题可选名称Subject Alternative Name)扩展用于列出证书有效的所有主机名。这个扩展曾经是可选的;如果它不存在,客户端将转而使用通用名称common name,CN)中提供的信息,该名称是主题字段的一部分。如果存在扩展名,则在验证过程中忽略CN字段的内容。

最后,最新添加的是证书透明性Certificate Transparency,CT)扩展,用于将日志记录证明携带到各种公共CT日志中。根据证书的生命周期,您可能会看到两到五个签名证书时间戳Signed Certificate Timestamps,SCTs)。对于识别证书有效性所必需的SCT的数量和类型,没有一套统一的要求。从技术上讲,每个客户都有权指定他们的期望。在实践中,Chrome是第一个需要CT的浏览器,其他客户端可能会效仿它。

密钥和证书转换

私钥和证书可以以多种格式存储,这意味着您通常需要将它们从一种格式转换为另一种格式。最常见的格式是:

PEM和DER转换

PEM和DER格式之间的证书转换是使用x509工具执行的。要将证书从PEM转换为DER格式:

要将证书从DER格式转换为PEM格式:

如果需要在DER和PEM格式之间转换私钥,语法是相同的,但使用了不同的命令: RSA密钥使用rsa,DSA密钥使用 dsa 。如果您正在处理新的PKCS #8格式,请使用 pkey 工具。

PKCS #12(PFX)转换

将PEM格式的密钥和证书转换为PKCS#12只需一个命令。以下示例将密钥(fd.key)、证书(fd.crt)和中间证书(fd-chain.crt)转换为等效的单个PKCS#12文件:

反向转换并不那么简单。您可以使用单个命令,但在这种情况下,您将在一个文件中获得全部内容:

现在,您必须在您最喜欢的编辑器中打开文件 fd.pem ,并手动将其拆分为单独的密钥、证书和中间证书文件。当你这样做的时候,你会注意到在每个组件之前提供了额外的内容。例如:

这个额外的元数据对于快速识别证书非常方便。显然,您应该确保主证书文件包含叶子服务器证书,而不是其他证书。此外,您还应该确保以正确的顺序提供中间证书,签发证书在签名证书之后。如果你看到一个自签名的根证书,可以随意删除或将其存储在其他地方;它不应该进入链条。

可以让OpenSSL为您拆分组件,但这样做需要多次调用pkcs12命令(包括每次键入捆绑包密码):

这种方法不会为您节省太多工作。您仍然必须检查每个文件,以确保它包含正确的内容并删除元数据。

PKCS #7 转换

要从PEM转换为PKCS#7,请使用 crl2pkcs7 命令:

要从PKCS#7转换为PEM,请使用带有-print_certs开关的pkcs7命令:

PKCS#12 的转换类似,您现在必须编辑 fd.pem 文件进行清理,并将其拆分为所需的组件。

配置

TLS服务器配置中的一个常见任务是选择要使用的密码套件。为了安全地通信,TLS需要决定使用哪些加密原语来实现其目标(例如机密性)。这是通过选择合适的密码套件来实现的,该套件会对如何进行身份验证、密钥交换、加密和其他操作做出一系列决定。依赖OpenSSL的程序通常采用与OpenSSL相同的套件配置方法,只需传递配置选项即可。

在TLS 1.3之前,通常的服务器配置将包括密码套件配置和服务器在协商过程中首选更强套件的选项。由于TLS 1.3的设计与早期协议版本存在一些差异,OpenSSL决定对其进行不同的配置,从而增加了服务器配置的复杂性。我将在以下部分讨论这个问题。

制定一个好的套件配置可能会非常耗时,而且有很多细节需要考虑。我写这一节是为了实现两个目标。如果你不想花很多时间学习如何使用OpenSSL以及如何对密码套件进行排名,只需使用我提供的默认配置即可。另一方面,如果您想了解OpenSSL配置的来龙去脉,本节将提供答案。

获取支持的套件

让我们从确定OpenSSL安装支持哪些套件开始深入研究。为此,使用 -v 开关和 ALL:COMPLEMENTOFALL 作为参数调用密码命令:

此时,您将看到很多输出,包括您安装的OpenSSL所提供的一切。就我而言,输出中有162套。让我们来看一行:

每一行输出都提供了一个套件的扩展信息。从左到右:

  1. 套件名称

  2. 所需的最小协议版本

    列表中的一些套件在协议列中显示SSLv3。这没什么好担心的。这只意味着该套件与这个旧的(和过时的)协议版本兼容。如果使用这些套件,您的配置将不会降级到SSL 3.0。

  3. 密钥交换算法

  4. 认证算法

  5. 加密算法和强度(strength)

  6. MAC(完整性)算法

传统上,OpenSSL不使用官方套件名称,尽管现在TLS 1.3套件使用了官方套件名称。最近,当您将 -stdname 开关添加到密码工具中时,您将同时获得officia套件名称和OpenSSL名称。

了解安全级别

在上一节中,我们讨论了如何获得支持套件的完整列表,但该列表具有欺骗性。仅仅因为支持某些内容并不意味着它将被启用。除非您另有说明,否则密码命令甚至会输出不允许的套件。诀窍是使用 -s 开关,之后套件数量将从162个减少到只有77个。

【FreeBSD是64个,Debian是74个】

当然,这一减少在很大程度上是由于删除了PSK和SRP套件,删除了66个条目(降至96个)。剩下的21个条目的差异是由于OpenSSL现在使用的安全级别概念造成的。

密码套件配置很复杂,大多数人都不是密码学专家。例如,很容易遵循互联网上一些过时的建议,在配置中添加不安全的元素。此外,有些安全方面无法通过密码套件配置,以前根本无法控制。因此,安全级别旨在保证最低安全要求,即使请求了不正确的配置。它们是一个非常有用的安全网。

OpenSSL安全级别表:

安全级别含义
Level 0没有限制。允许在编译时启用所有功能。不安全的
Level 1安全级别对应于最低80位的安全性。
Level 2安全级别设置为112位安全级别。
Level 3安全级别设置为128位安全级别。
Level 4安全级别设置为192位安全级别。
Level 5安全级别设置为256位安全级别。

安全级别最重要的方面是知道不使用什么,这是前两个级别。级别0没有限制,并且可能不安全(取决于在编译时启用了哪些功能)。在实践中,你不太可能需要这个级别。级别1稍好一些,但仍然允许弱元素。出于与遗留系统的互操作性目的,您可能需要此级别。级别1是OpenSSL中的默认安全级别。

在实践中,你应该以2级为基准。此级别支持2048位RSA密钥,目前大多数网站都在使用。不允许使用SSL 2和SSL 3等弱协议,以及RC4和SHA1。如果你没有使用OpenSSL,你可能会发现你的发行版已经为你做出了这个选择;例如,Ubuntu 20.04 LTS选择级别2作为默认值。

如果您有特定的安全要求并希望实施更强大的加密,则应考虑级别3及以上。例如,如果启用级别3,则不允许使用2048位RSA密钥。因为比这更强的密钥非常慢,所以这种安全级别的选择隐式地将您限制为ECDSA密钥。

通过在套件配置中使用 -s 开关和 @SECLEVEL 关键字,您可以更好地了解安全级别。例如,让我们看看从级别3切换到级别4如何影响一个任意的套件配置。在级别3,输出中有四个套件:

然而,在级别4,输出中只有两个套件,因为删除了128位套件:

您可能已经注意到,前面的示例引入了 -tls1_2 开关,它只输出可以与TLS 1.2协商的套件。当您只对一种协议感兴趣时,此开关以及 -tls1_3-tls1_1-tls1-ssl3 对于删除不需要的输出非常有用。

配置TLS 1.3

如果您正在使用密码工具,并且不熟悉TLS 1.3的配置方式(例如,您只使用不支持此协议的OpenSSL版本),您可能会对以下事实感到困惑:无论您指定什么配置,TLS 1.3套件总是列在顶部。这是因为OpenSSL为TLS 1.3套件配置引入了一种单独的机制。在库级别,有单独的函数调用,并且有单独的方法与命令行工具一起使用。

当涉及到密码工具时,要控制TLS 1.3套件,您需要使用 -ciphersuites 密码套件开关。为了说明这一点,让我们启用一个TLS 1.3套件和一个SEED套件:

当他们为TLS 1.3添加这种新的配置机制时,OpenSSL开发人员抓住机会通过删除各种工具和关键字来简化套件的配置,这些工具和关键字现在可以称为遗留套件配置。TLS 1.3唯一支持的方法是按照您希望支持的顺序提供一个冒号分隔的套件列表。仅此而已。例如:

这种TLS 1.3配置的新方法对现实生活有何影响?根据您的工具,您现在可能会发现自己需要使用两个配置字符串,而以前只有一个。在Apache web服务器中,SSLCipherSuite 指令已通过可选的第一个参数进行了扩展,使您能够针对要配置的协议。所以你可以这样做:

结果将相当于以下内容:

并非所有工具都添加了对TLS 1.3套件配置的支持。相反,您总是得到OpenSSL默认值。对于大多数用户来说,这还不是一个真正的问题,因为所有TLS 1.3套件都很强大。但是,如果你想做一些不同寻常的事情,也许可以启用当前默认禁用的 CCM 套件,你必须通过配置文件更改OpenSSL默认值来使用一种解决方法,我将在下一节中介绍。

配置OpenSSL默认值

偶尔,您在尝试配置某些应用程序以某种方式使用OpenSSL时会遇到问题,如果没有配置选项来实现您所需的功能,您会感到沮丧。在这种情况下,您可以更改OpenSSL默认值。

启动时,OpenSSL将经历一个初始化过程,尝试从文件系统中获取默认值。该程序包括以下步骤:

  1. 检查 OPENSSL_CONF 环境变量,该变量应包含配置文件的路径。如果二进制文件设置了 setuidsetguid 标志,则跳过此步骤。
  2. 如果失败,则检查编译时指定的配置目录的默认系统范围位置。OpenSSL将在该文件夹中查找名为 openssl.cnf的文件。

此过程确保有许多选项可用于以解决特定需求的方式控制默认值。我们可以仅更改一个程序或在同一服务器上运行的所有程序的默认配置。

对于后一种用例,使用版本工具确定默认配置文件的位置:

现在我们知道如何更改默认值,问题就变成了在配置文件中放入什么。有关配置文件的语法和详细信息,最好查阅官方文档。但是,如果您只需要重新配置密码套件配置,请查看以下示例:

此配置文件指定了支持的最低协议、安全级别、传统密码套件配置和TLS 1.3套件配置,它还启用了特殊的ChaCha20优先级,如果OpenSSL检测到客户端更喜欢此密码而不是AES,则会触发此优先级。有关可用参数的完整列表,请参阅官方文档。

建议的套件配置

当涉及到密码套件配置时,最好的方法是避免传统的基于关键字的套件配置,而是明确指定要使用的套件。通过这样做,您不必了解复杂的关键字行为,可以最大限度地减少错误,并且还可以留下一个自文档化且易于理解的配置。

在本节中,我将向您提供我的建议并解释我的推理。为了简单起见,我将把这些套件显示为一个有序的列表,尽管它们分别针对TLS 1.3和早期协议版本进行了配置。以下是我为所有TLS服务推荐的默认配置,按优先顺序列出套件:

此配置仅使用支持前向保密(forward secrecy)并提供强加密的套件。首选128位套件,它更快,提供强安全ECDSA公钥加密,比传统RSA(在通常的密钥长度下)更快、更安全;ECDHE密钥交换,比DHE更快;以及比旧CBC模式更快、更安全的认证加密。

可以采用更短的套件配置,例如,删除256位套件以及使用DHE进行密钥交换的套件。然而,我发现,拥有稍微多样化的套件有助于避免挑剔客户的各种边缘情况。

在互操作性方面,所有现代浏览器和客户端都应该能够连接。一些非常老的客户端可能不会,但我们谈论的是过时的平台,例如在Windows XP上运行的Internet Explorer。如果您真的需要支持此功能,则需要将使用过时功能(如3DES或RSA密钥交换)的套件附加到列表中。

最后,在性能方面,您可以使用最后一个技巧:告诉OpenSSL对更喜欢这种密码而不是AES的客户端使用ChaCha20。您会注意到,在我的配置中,总是有一个ChaCha20套件遵循128位AES-GCM套件。对于大多数客户来说,AES-GCM是正确的选择,但ChaCha20对于一些移动客户端来说是更好的选择,因为它们可以更快地完成。通过ChaCha20优先级,您可以为这些移动客户端提供更好的体验(更快的加载时间)。

OpenSSL为此提供的选项称为 PrioritizeChaCha 。此功能是一个相对较新的配置选项,您会发现并非所有服务器软件都可以控制它。例如,在撰写本文时,Apache可以(使用 SSLOpenSSLConfCmd ),但Nginx不能。如前一节所述,在后一种情况下,通过更改OpenSSL默认值应该可以解决问题。

生成DH参数

DH密钥交换已经过时,但从哲学角度来看,您可能仍然希望在服务器中支持它。如果你这样做,你可能会发现一些服务器软件(例如Nginx)需要手动配置所需的DH参数。这是如何:

在实践中,只有2048位DH参数是有意义的。任何少的东西都会让你变得软弱或不安全,而任何多的东西都将让你放慢速度。DH参数不需要保密。事实上,有一些预定义的组(有时称为众所周知的组)是推荐的,因为已知它们是安全生成的。

很少,您可能会遇到这样的情况,通常是在传统环境中,您需要使用1024位DH参数配置服务器。在这种情况下,你不能使用一个知名的组。问题是弱DH组容易受到预计算攻击,这进一步降低了它们的安全性。如果您真的必须使用1024位DH参数,请始终使用OpenSSL生成您自己的唯一组。

旧套件配置

在本节中,我将简要介绍适用于TLS 1.2和早期协议版本的基于遗留关键字的密码套件配置。只有当你对关键字方法的工作原理感兴趣时,这一部分才很重要。否则,您最好简单地指定要使用的套件,就像我在上一节中推荐的配置一样。

关键字

密码套件 keywords(关键字)是密码套件配置的基本构建块。每个套件名称(例如RC4-SHA)都是一个关键字,可以选择一个套件。所有其他关键字都根据一些标准选择套件组。关键字名称区分大小写。在本节中,我将逐一概述OpenSSL支持的所有密码套件关键字。

关键字含义
DEFAULT默认密码列表。这是在编译时确定的,并且必须是指定的第一个密码字符串。
COMPLEMENTOFDEFAULTALL中包含的密码,但默认情况下未启用。请注意,此规则不包括 eNULLeNULL 不包含在 ALL 中(必要时使用 COMPLEMENTOFALL )。
ALLeNULL 密码外的所有密码套件,必须显式启用。
COMPLEMENTOFALLALL(当前为 eNULL )未启用密码套件。
HIGH“High”-加密密码套件。这目前意味着密钥长度大于128位的那些,以及一些具有128位密钥的密码套件。
MEDIUM“Medium”-加密密码套件,目前其中一些使用128位加密。
LOW“Low”-加密密码套件,目前使用64位或56位加密算法,但不包括导出密码套件。不再支持。不安全
EXP,EXPORT导出加密算法。包括40位和56位算法。不再支持。
不安全的
EXPORT4040位导出加密算法。不再支持。不安全的
EXPORT5656位导出加密算法。不再支持。不安全的
TLSv1.2, TLSv1.0, TLSv1,
SSLv3, SSLv2
需要指定协议版本的密码套件。TLS 1.0有两个关键字,TLS 1.3和TLS 1.1没有关键字。这些关键字不会影响协议配置,只会影响套件。
关键字含义
MD5使用MD5的密码套件。过时且不安全
SHA, SHA1使用SHA1的密码套件。
SHA256使用SHA256的密码套件。
SHA384使用SHA384的密码套件。
关键字含义
aDH密码套件有效地使用DH身份验证,即证书携带DH密钥。在1.1.0中删除。
aDSS、DSS使用DSS身份验证的密码套件,即证书携带DSS密钥。
aECDH使用ECDH身份验证的密码套件。在1.1.0中删除。
aECDSA, ECDSA使用ECDSA身份验证的密码套件。
aNULL密码套件不提供身份验证。这是目前的匿名DH算法。不安全的
aRSA使用RSA身份验证的密码套件,即证书携带RSA密钥。
aPSK使用PSK(预共享密钥)身份验证的密码套件。
aSRP使用SRP(安全远程密码)身份验证的密码套件。
关键字含义
ADH匿名(anonymous)DH密码套件。不安全的。
AECDH匿名ECDH密码套件。不安全。
DHE, EDH仅使用临时DH密钥协商的密码套件。
ECDHE, EECDH使用临时ECDH的密码套件。
kDHE, kEDH, DH使用临时DH密钥协商的密码套件(包括匿名DH)。
kECDHE, kEECDH, ECDH使用临时ECDH密钥协商的密码套件(包括匿名ECDH)。
kRSA, RSA使用RSA密钥交换的密码套件。
kPSK, kECDHEPSK, kDHEPSK,
kRSAPSK
使用PSK密钥交换的密码套件。
关键字含义
AES, AESCCM, AESCCM8, AESGCM使用AES、AES CCM和AES GCM的密码套件。
ARIA, ARIA128, ARIA256使用ARIA的密码套件。
CAMELLIA, CAMELLIA128,
CAMELLIA256
使用Camellia的密码套件。过时的。
CHACHA20使用ChaCha20的密码套件。
eNULL, NULL不使用加密的密码套件。不安全的。
IDEA使用IDEA的密码套件。过时的。
SEED使用SEED的密码套件。过时的。
3DES, DES, IDEA, RC2, RC4默认情况下不再支持。过时且不安全。
关键字含义
@SECLEVEL配置安全级别,该级别设置最低安全要求。
@STRENGTH按加密算法密钥长度的顺序对当前密码套件列表进行排序。
aGOST使用GOST R 34.10(2001或94)进行身份验证的密码套件。需要具有GOST功能的引擎。
aGOST01使用GOST R 34.10-2001身份验证的密码套件。
aGOST94使用GOST R 34.10-94身份验证的密码套件。过时的。请改用GOST R 34.10-2001。
kGOST使用RFC 4357中指定的VKO 34.10密钥交换的密码套件。
GOST94使用基于GOST R 34.11-94的HMAC的密码套件。
GOST89MAC使用GOST 28147-89 MAC而不是HMAC的密码套件。
PSK在任何容量下使用PSK的密码套件。

组合关键字

在大多数情况下,您将单独使用关键字,但也可以通过将两个或多个关键字与 + 字符连接,将它们组合起来,只选择满足多种要求的套件。在以下示例中,我们选择将ECDHE密钥交换与AES-GCM结合使用的套件:

构建密码套件列表

构建密码套件配置的关键概念是当前套件列表(current suite list)。列表始终以空开头,没有任何套件,但您添加到配置字符串中的每个关键字都会以某种方式更改列表。默认情况下,新套件将附加到列表中。在以下示例中,配置从使用ECDHE密钥交换的所有套件开始,然后是使用DHE密钥交换的全部套件:

冒号字符通常用于分隔关键字,但空格和逗号同样可以接受。以下命令产生与前一个示例相同的输出:

关键字编辑器

关键字编辑器是您可以放置在每个关键字开头的字符,以便将默认操作(添加到列表中)更改为其他操作。支持以下操作:

排序

@STRENGTH 关键字有一个特殊的用途:它不会引入或删除任何套件,但会按照密码强度降序对其进行排序。自动排序是一个有趣的想法,但它只有在一个完美的世界里才有意义,在这个世界里,密码套件实际上可以仅通过密码强度进行比较。在大多数情况下,通常不需要最高强度的套件。您经常在配置中使用它们,只是为了与挑剔的客户端进行互操作。

处理错误

在配置过程中,您可能会遇到两种类型的错误。第一种是拼写错误或试图使用不存在的关键字的结果:

输出很神秘,但它确实包含一条错误消息。

另一种可能性是,您最终会得到一个空的密码套件列表,在这种情况下,您可能会看到类似于以下内容的内容:

性能

如您所知,计算速度是任何加密操作的重要限制因素。OpenSSL附带了一个内置的基准测试工具,您可以使用它来了解系统的功能和限制。您可以使用 speed 命令调用基准。

如果您在不使用任何参数的情况下调用 speed ,OpenSSL会产生大量输出,其中很少有人会感兴趣。更好的方法是只测试与您直接相关的算法。例如,在安全的web服务器中使用时,您可能会关心RSA和ECDSA的性能,并会这样做:

结果输出的第一部分由OpenSSL版本号和编译时配置组成。此信息对于记录保存非常有用,如果您正在测试不同版本的OpenSSL:

输出的其余部分包含基准测试结果。让我们先来看看RSA密钥操作:

RSA最常用于2048位。在我的结果中,被测试服务器的一个CPU每秒可以执行大约1000个签名(服务器)操作和22000个验证(客户端)操作。至于ECDSA,它通常只使用256位。我们可以看到,在这个长度上,ECDSA可以完成10倍的签名。另一方面,在验证方面速度较慢,每秒仅6500次操作:

【在我的NAS中:

NAS为双核,当使用 -multi 2 参数时,签名速度和验证速度分别有近93%左右的提升:

在飞牛NAS中(四核四线程的i3-9300,运行时CPU温度达到100℃)使用 -multi 4 ,结果如下:

AWS云主机使用 -multi 2 参数的结果如下:

在实践中,您更关心签名操作,因为服务器旨在为许多客户端提供服务。另一方面,客户端通常同时只与少数服务器通信。在这种情况下,ECDSA较慢的事实并不重要。

这个速度输出有什么用?您应该能够比较编译时选项如何影响速度,或者不同版本的OpenSSL如何在同一平台上进行比较。

如果你想切换服务器,对OpenSSL进行基准测试可以让你了解计算能力的差异。您还可以验证硬件加速是否到位。

使用基准测试结果来估计部署性能并不简单,因为现实生活中影响性能的因素很多。此外,其中许多因素都在TLS之外(例如,HTTP保持活动设置、缓存等)。充其量,这些数字只能用于粗略估计。

但在你这样做之前,你需要考虑其他事情。默认情况下, speed 命令将仅使用单个进程。大多数服务器都有多个内核,因此要了解整个服务器支持多少TLS操作,您必须指示速度并行使用多个实例。您可以使用 -multi 开关来实现这一点。我的服务器有两个核心,所以这就是我要指定的:

正如预期的那样,性能大约好两倍。我再次关注每秒可以完成多少RSA签名,因为这是服务器上执行的CPU密集型加密操作,因此始终是第一个瓶颈。1988个签名/秒(使用2048位密钥)的结果告诉我们,这个小型服务器每秒肯定会处理数百个全新的TLS连接。(我们必须假设服务器会做其他事情,而不仅仅是TLS握手。)就我而言,这就足够了——有一个非常健康的安全裕度。因为我在服务器上也启用了会话恢复,并且绕过了公共加密,所以我知道性能会更好。

在测试速度时,务必使用 -evp 开关启用硬件加速。如果你不这样做,结果可能会大不相同。作为示例,看看支持AES-NI硬件加速的服务器上的性能差异。我通过纯软件实现得到了以下结果:

硬件加速后,性能提高了三倍多:

当你考虑加密操作的速度时,你应该关注你实际部署的原语。例如,CBC已经过时,所以您想在GCM模式下使用AES。在这里,我们看到GCM的性能提高了三到四倍:

然后是ChaCha20-Poly1305,这是一个相对较新的添加。它的性能无法与硬件加速的AES竞争,但它不需要;这种经过身份验证的密码在手机上设计得很快。将其速度与未加速的AES-128-CBC进行比较。

创建私有证书颁发机构

如果你想设置自己的CA,你需要的一切都已经包含在OpenSSL中。用户界面纯粹是基于命令行的,因此对用户不太友好,但这可能会更好。经历这个过程是非常有教育意义的,因为它迫使你思考每一个方面,甚至是最小的细节。

设置私人CA的教育方面是我建议这样做的主要原因,但还有其他原因。基于OpenSSL的CA,尽管可能很粗糙,但可以很好地满足个人或小团体的需求。例如,在开发环境中使用私有CA比在任何地方使用自签名证书要好得多。同样,提供双因素身份验证的客户端证书可以显著提高敏感web应用程序的安全性。

运行私有CA的最大挑战不是设置所有内容,而是保持基础设施的安全。例如,根密钥必须保持脱机状态,因为所有安全都依赖于它。另一方面,CRL和OCSP响应者证书必须定期刷新,这需要使根联机。

在阅读本节时,您将创建两个配置文件:一个用于控制根CA( root-ca.conf ),另一个用于管理从属CA( sub-ca.conf )。虽然你只需按照我的说明就可以从头开始做任何事情,但你也可以从我的GitHub帐户下载配置文件模板。后一种选择会节省你一些时间,但前一种方法会让你更好地理解所涉及的工作。

功能和限制

在本节的其余部分,我们将创建一个在结构上与公共CA相似的私有CA。将有一个根CA,可以从中创建其他从属CA。我们将通过CRL和OCSP响应程序提供撤销信息。为了使根CA保持离线,OCSP响应者将拥有自己的身份。这不是你能拥有的最简单的私有CA,但它是一个可以正确保护的CA。作为奖励,下级CA将受到技术限制(technically constrained),这意味着它只允许为允许的主机名颁发证书。

安装完成后,必须将根证书安全地分发给所有预期的客户端。一旦根就绪,您就可以开始颁发客户端和服务器证书。这种设置的主要局限性是OCSP响应器主要是为测试而设计的,只能用于较轻的负载。

创建根CA

创建新的CA涉及几个步骤:配置、创建目录结构和初始化密钥文件,最后生成根密钥和证书。本节描述了该过程以及常见的CA操作。

根CA配置

在实际创建CA之前,我们需要准备一个配置文件( root-ca.conf ),该文件将告诉OpenSSL我们希望如何设置。在正常使用过程中,大多数时候不需要配置文件,但在涉及复杂操作(如创建根CA)时,它们是必不可少的。OpenSSL配置文件功能强大;在继续之前,我建议您熟悉它们的功能(命令行上的 man config )。

配置文件的第一部分包含一些基本的CA信息,如名称和基本URL,以及CA可分辨名称的组成部分。由于语法灵活,信息只需要提供一次:

第二部分直接控制CA的运营。有关每个设置的完整信息,请参阅ca命令的文档(命令行上的 man ca )。大多数设置都是不言自明的;我们主要告诉OpenSSL我们想把文件放在哪里。因为这个根CA将仅用于颁发下级CA,所以我选择证书的有效期为10年。对于签名算法,默认情况下使用安全的SHA256。

默认策略( policy_c_o_match )已配置,从此CA颁发的所有证书都具有与CA匹配的 countryNameorganizationName 字段。这通常不会由公共CA完成,但适用于私有CA:

第三部分包含 req 命令的配置,该命令在创建自签名根证书期间只使用一次。最重要的部分在扩展中:basicConstraints 扩展表示证书是CA,keyUsage 包含此场景的适当设置:

配置文件的第四部分包含在构建根CA颁发的证书期间将使用的信息。所有证书都将是CA,如 basicConstraints 扩展所示,但我们将 pathlen 设置为零,这意味着不允许有更多的从属CA。

所有下级CA都将受到限制,这意味着他们颁发的证书仅对域名的一个子集有效,并且使用受到限制。首先,扩展的 KeyUsage 扩展仅指定了 clientAuthserverAuth ,即TLS客户端和服务器身份验证。其次,nameConstraints 扩展将允许的主机名限制为 example.comexample.org 域名。理论上,这种设置使您能够将从属CA的控制权交给其他人,但仍然可以安全地知道他们不能为任意主机名颁发证书。如果需要,可以将每个从属CA限制为一个小的域命名空间。排除这两个IP地址范围的要求来自CA/浏览器论坛的基线要求,该要求对技术上受限制的下属CA进行了定义。

在实践中,名称约束并不完全实用,因为一些主要平台目前无法识别 nameConstraints 扩展。如果您将此扩展标记为关键,则此类平台将拒绝您的证书。如果你不将其标记为关键(如示例中所示),你就不会遇到这样的问题,但其他一些平台不会强制执行。

配置的第五部分也是最后一部分指定了与OCSP响应签名证书一起使用的扩展。为了能够运行OCSP响应程序,我们生成了一个特殊的证书,并将OCSP签名功能委托给它。此证书不是CA,您可以从扩展中看到:

根CA目录结构

下一步是创建上一节中指定的目录结构,并初始化CA操作期间将使用的一些文件:

使用以下子目录:

根CA生成

我们采取两个步骤来创建根CA。首先,我们生成密钥和CSR。当我们使用 -config 开关时,所有必要的信息都将从配置文件中获取:

在第二步中,我们创建一个自签名证书。-extensions 开关指向配置文件中的 ca_ext 部分,该部分激活适用于根ca的扩展:

数据库文件的结构

db/index 中的数据库是一个明文文件,其中包含证书信息,每行一个证书。创建根CA后,它应该只包含一行:

每行包含六个由制表符分隔的值:

  1. 状态标志(V表示valid——有效,R表示revoked——已撤销,E表示expired——已过期)
  2. 有效期至(以YYMMDDHHMMSSZ格式)
  3. 撤销日期,如果未撤销,则为空
  4. 序列号(16进制)
  5. 文件位置,unknown 表示不知道文件位置
  6. 可分辨名称(Distinguished name)

根CA操作

要从新CA生成CRL,请使用CA命令的 -gencrl 开关:

要颁发证书,请使用所需的参数调用ca命令。重要的是, -extensions 开关指向配置文件中的正确部分(例如,您不想创建另一个根CA)。

要吊销(revoke)证书,请使用 ca 命令的 -revoke 开关;您需要一份要吊销的证书副本。因为所有证书都存储在 certs/ 目录中,所以您只需要知道序列号。如果你有一个可分辨的名字,你可以在数据库中查找序列号。

-crl_reason 开关中的值选择正确的原因。该值可以是以下值之一:

创建OCSP签名证书

首先,我们为OCSP响应者创建一个密钥和CSR。这两个操作与任何非CA证书一样完成,这就是为什么我们不指定配置文件:

其次,使用根CA颁发证书。-extensions 开关的值指定 ocsp_ext ,这可确保设置适合OSCP签名的扩展。我将新证书的有效期从默认的3650天缩短到365天。因为这些OCSP证书不包含吊销信息,所以无法吊销。因此,你希望尽可能缩短生命周期。一个好的选择是30天,前提是您准备生成一个新的证书,通常:

现在,您已经准备好启动OCSP响应程序。对于测试,您可以在根CA所在的同一台机器上进行。但是,对于生产,您必须将OCSP响应程序密钥和证书移动到其他地方:

您可以使用以下命令行测试OCSP响应程序的操作:

在输出中,verify OK 表示签名已正确验证,good 表示证书未被吊销。

创建下级CA

下级CA生成过程在很大程度上反映了根CA过程。在本节中,我将仅在适当的情况下强调差异。有关其他内容,请参阅上一节。

下级CA配置

要为从属ca生成配置文件(sub-ca.conf),请从我们用于根CA的文件开始,并进行本节中列出的更改。我们将把名称更改为 subca ,并使用不同的可分辨名称(distinguished name)。我们将把OCSP响应器放在不同的端口上,但这只是因为 ocsp 命令不理解虚拟主机。如果您为OCSP响应程序使用了合适的web服务器,则可以完全避免使用特殊端口。新证书的默认生存期为365天,我们将每30天生成一次新的CRL。

copy_extensions 更改为 copy 意味着CSR的扩展将被复制到证书中,但前提是它们尚未在我们的配置中设置。通过此更改,无论谁准备CSR,都可以在其中添加所需的替代名称,并且其中的信息将被提取并放置在证书中。此功能有点危险(您允许其他人对证书中的内容进行有限的直接控制),但我认为它适用于较小的环境:

在配置文件的末尾,我们将添加两个新的配置文件,分别用于客户端和服务器证书。唯一的区别在于 keyUsageextendedKeyUsage 扩展。请注意,我们指定了 basicConstraints 扩展,但将其设置为 false 。我们这样做是因为我们正在从CSR复制扩展。如果我们省略了这个扩展,我们最终可能会使用CSR中指定的一个:

在您对配置文件满意后,按照与根CA相同的过程创建一个目录结构。只需使用不同的目录名,例如 sub-ca

下级CA生成

如前所述,我们采取两个步骤来创建从属CA。首先,我们生成密钥和CSR。当我们使用 -config 开关时,所有必要的信息都将从配置文件中获取。

在第二步中,我们让根CA颁发证书。-extensions 开关指向配置文件中的 sub_ca_ext 部分,该部分激活适用于从属CA的扩展。

下属CA操作

要颁发服务器证书,请在 -extensions 开关中指定 server_ext 的同时处理CSR:

要颁发客户端证书,请在 -extensions 开关中指定 client_ext 的同时处理CSR:

CRL生成和证书吊销与根CA相同。OCSP响应器唯一不同的是端口;下级CA应改用 9081 。建议响应者使用自己的证书,这样可以避免将从属CA保留在公共服务器上。



使用OpenSSL测试TLS

由于大量的协议功能和实现怪癖,有时很难确定安全服务器的确切配置和功能。尽管存在许多用于此目的的工具,但通常很难确切地知道它们是如何工作的,这有时使得很难完全信任它们的结果。尽管我花了数年时间测试安全服务器并获得了良好的工具,但当我真的想了解发生了什么时,我会使用OpenSSL和Wireshark。我并不是说你应该在日常测试中使用OpenSSL;相反,你应该找到一个你信任的自动化工具。对于在线测试,我建议使用 Hardenize ;对于离线工作,可以考虑 testssl.sh 。但是当你真的需要确定某事时,唯一的办法就是使用OpenSSL。

自定义编译OpenSSL以进行测试

最近,将OpenSSL用于测试目的变得更加困难,因为矛盾的是,OpenSSL本身变得更好了。在Heartbleed事件之后,OpenSSL开发人员进行了一次彻底的改革,其中一个方面是删除了过时的加密技术。当然,这对每个人来说都是个好消息,但确实让我们的生活变得更加困难。为了测试各种各样的条件,我们可能需要使用两个版本:一个是最新版本,一个是旧版本。最近的版本对于测试现代功能(例如TLS 1.3)很有用,但旧版本是测试过时功能所需要的。

在撰写本文时,新版本肯定来自1.1.1分支。至于旧版本,经过一些研究,我决定使用OpenSSL 1.0.2g,配置后可以恢复删除一些过时的功能:

在本章中,我将把这两个版本的OpenSSL称为 new(新版本)和 old(旧版本)。这就是你如何知道要使用哪个版本进行测试。有关如何配置和安装OpenSSL的更多信息,请参阅上一章。

连接到TLS服务

OpenSSL附带了一个客户端工具,您可以使用它连接到安全的服务器。该工具类似于 telnetnc ,因为它处理加密方面,但允许您完全控制接下来的层。

要连接到服务器,您需要提供主机名和端口。例如:

请注意,您必须提供两次主机名。-connect 开关用于建立TCP连接,但 -servername 用于指定在TLS级别发送的主机名。从OpenSSL 1.1.1开始,s_client 工具会自动配置后者。如果(1)您使用的是早期版本的OpenSSL,(2)您连接到IP地址,或(3)TLS主机需要不同,您仍然需要使用 -servername 开关。使用 -noservername 开关避免在TLS握手中发送主机名信息。

键入命令后,您将看到大量诊断输出(稍后将详细介绍),然后有机会键入您想要的任何内容。因为我们正在与HTTP服务器交谈,所以最明智的做法是提交HTTP请求。在以下示例中,我使用 HEAD 请求,因为它指示服务器不要发送响应正文:

现在我们知道TLS通信层正在工作:我们接通了HTTP服务器,提交了一个请求,并收到了回复。让我们回到诊断输出。前几行将显示有关服务器证书的信息:

输出中的下一部分按交付顺序列出了服务器提供的所有证书:

对于每个证书,第一行显示主题,第二行显示颁发者信息。

当您需要查看发送的证书时,这部分非常有用;浏览器证书查看器通常显示重建的证书链,这些证书链几乎与呈现的证书链完全不同。要确定链在名义上是否正确,您可能希望验证主体和发行者是否匹配。您从顶部的叶子(web服务器)证书开始,然后沿着列表往下走,将当前证书的颁发者与下一个证书的主题进行匹配。您看到的最后一个颁发者可以指向不在链中的某个根证书,或者——如果包含自签名根证书——它可以指向自己。

输出中的下一项是服务器证书;这是很多文本,但为了简洁起见,我将删除其中的大部分:

如果你想更好地查看证书,你首先需要从输出中复制它并将其存储在一个单独的文件中。我将在下一节讨论如何做到这一点。

以下是关于TLS连接的大量信息,其中大部分是不言而喻的:

这里最重要的信息是协议版本(TLS 1.2)和使用的密码套件(ECDHE-RSA-AES128-GCM-SHA256)。请注意,协议信息出现在两个位置,当显示不同版本时,这可能会造成混淆。第一个位置描述了协商的密码套件的最低协议要求,而第二个位置指向当前正在协商的实际协议版本。您将看到一些旧密码套件的协议版本存在差异,例如:

所选套件可以与SSL 3.0一起使用,但在此连接上与TLS 1.2一起使用:

您还可以确定服务器已向您发出会话ID和TLS会话票证(一种在不让服务器保持状态的情况下恢复会话的方法),并且支持安全的重新协商。

证书验证

仅仅因为您能够连接到TLS服务器,并不意味着服务配置正确,即使服务器支持所有正确的协议和密码套件。配置的证书与正确的DNS名称匹配同样重要。

默认情况下, s_client 工具会报告但忽略证书问题。此外,在你开始相信它的判断之前,你需要相信它在看到有效证书时能够识别出来。当您使用自定义编译的二进制文件时,尤其如此。

在上一节的示例中,验证状态代码(显示在倒数第二行)为0,这意味着验证已成功。如果您正在连接到具有有效公共证书的服务器,但您看到的是状态20,这可能意味着受信任的根没有正确配置:

此时,如果你不想修复你的OpenSSL安装,你可以使用 -CApath 开关指向保存根目录的位置。例如:

如果您有一个包含根目录的单个文件,请使用 -CAfile 开关:

即使此时您获得了成功的状态代码,也不意味着证书配置正确。这是因为 s_client 工具不会检查给定主机名的证书是否正确;您必须告诉它手动执行此操作,并告诉它使用哪个主机名:

如果不匹配,您可能会看到状态代码62:

否则,您将看到熟悉的状态代码0。在极少数情况下,您需要验证为IP地址而不是主机名颁发的证书,您需要使用 -verify_IP 开关进行验证。

测试升级到TLS的协议

当与HTTP一起使用时,TLS会封装整个明文通信通道以形成HTTPS。其他一些协议从明文开始,但随后升级为加密。如果你想测试这样的协议,你必须告诉OpenSSL它是哪种协议,以便它可以代表你进行升级。使用 -starttls 开关提供协议信息。例如:

在撰写本文时,最新OpenSSL版本支持的协议是 smtppop3imapftpxmppxmpp-serverircpostgresmysqllmtpnntpsieveldap 。OpenSSL 1.0.2g的选择较少:smtppop3imapftpxmpp

某些协议要求客户端提供其名称。例如,对于SMTP,默认情况下OpenSSL将使用 mail.example.com ,但您可以使用 -name 开关指定正确的值。如果您正在测试XMPP,可能需要指定正确的服务器名称;您可以使用 -xmpphost 开关来实现这一点。

提取远程证书

当您使用 s_client 连接到远程安全服务器时,它会将服务器的PEMencoded证书转储到标准输出。如果出于任何原因需要证书,可以从回滚缓冲区复制它。如果您事先知道只想检索证书,则可以使用此命令行作为快捷方式:

echo 命令一开始的目的是将shell与 s_client 分离。如果不这样做,s_client 将等待您的输入,直到服务器超时(这可能需要很长时间)。

默认情况下, s_client 只打印叶子证书;如果你想打印整个链,给它打 -showcerts 开关。启用该开关后,上一个命令行将把所有证书放在同一个文件中。

另一个有用的技巧是将 s_client 的输出直接传输到 x509 工具。以下命令显示了详细的服务器信息及其SHA256指纹:

有时,您需要获取证书指纹并将其与其他工具一起使用。不幸的是,OpenSSL以显示单个字节并使用冒号分隔它们的格式输出证书。这个方便的命令行通过删除冒号并将十六进制字符转换为小写来规范证书指纹:

测试协议支持

默认情况下,s_client 将尝试使用最佳协议与远程服务器通信,并在输出中报告协商的版本。如前所述,您将在输出中两次找到协议版本,并且您需要明确谈论协议的行:

如果您需要测试对特定协议版本的支持,您有两个选择。您可以通过提供 -ssl2-ssl3-tls1-tls_1-tls_2tls1_3 开关之一来显式选择一个协议进行测试。当然,每个开关都需要在测试工具中支持特定的协议版本。如果你想从测试中排除特定的协议,有一系列开关可以禁用协议(例如,TLS 1.2的 -no_tls_1_2 )。有时,这可能是更好的方法。从OpenSSL 1.1.0开始,有两个新选项,-min_protocol-max-protocol ,分别控制最低和最高协议版本。

例如,在测试不支持特定协议版本的服务器时,您可能会得到以下输出:

了解服务器是否支持SSL 2.0有时可能需要更多的工作,因为这个旧的、非常不安全的SSL协议版本使用的握手与SSL 3.0以后使用的握手不同。虽然现在只支持SSL 2.0的服务器应该非常罕见,但要检查这种可能性,您需要使用 -ssl2 开关提交一个单独的检查。

另一个协议差异是,SSL 2.0服务器有时没有配置任何密码套件。在这种情况下,虽然支持SSL 2.0,但从技术上讲,任何握手尝试都会失败。您应该将这种情况视为配置错误。

测试密码套件配置

您不太可能花费大量时间在命令行上使用OpenSSL测试密码套件配置。这是因为一次只能有效地测试一个套件;对TLS 1.2和早期协议修订版支持的300多个密码套件进行测试将需要相当长的时间。这是一个使用这些方便的工具来自动化流程的绝佳机会。

不过,有时您需要探测服务器,以确定它们是否支持特定的套件或加密原语,或者首选项是否配置正确。

TLS 1.3的引入使该领域的测试稍微复杂一些,但仍然可以管理。由于此协议版本与所有其他版本之间的差异,通常最好将测试分为两组。在测试TLS 1.3时,请始终将 -cryptsuites 开关与 -tls1_3 结合使用。通常的方法是只指定一个套件来确定它是否受支持:

如果选择不支持的套件,输出自然会有所不同:

当您测试TLS 1.2和早期协议版本的配置时,请将 -crepher 开关与 -no-tls1_3 结合使用(假设您使用的OpenSSL版本支持TLS 1.3):

正如您在前面的示例中看到的,在测试TLS 1.2及更早版本时,您不必只指定一个密码套件,但在这种情况下,您需要观察协商的内容。如果你想进一步探索,你总是可以调整命令行以删除之前协商过的套件:

即使您不会手动测试大量套件,也有一种快速的方法可以确定特定服务器是否支持许多糟糕的加密原语中的任何一个。为此,请使用旧的OpenSSL版本并列出所有错误的密码套件关键字,如下所示:

另一个很好的测试是查看服务器是否支持不支持前向保密的RSA密钥交换:

理想情况下,你会在这里遇到握手失败,但如果你没有,这并不可怕,只要服务器只将RSA密钥交换作为最后手段。您可以通过提供具有前向保密功能的套房作为您最不喜欢的选择来检查这一点:

测试密码套件首选项

作为一般规则,TLS服务器应始终配置为强制执行其密码套件偏好,确保它们与每个客户端协商其首选密码套件。此功能对于TLS 1.2和更早的协议修订版至关重要,这些修订版支持许多密码套件,其中大多数都是不理想的。TLS 1.3的情况则不同:目前只有少数套件可用,而且所有套件都是安全的,因此强制服务器偏好并不重要。

要测试服务器套件偏好,您首先需要了解支持哪些套件。例如,您可以拥有支持的套件的完整列表。或者,您可以使用不同的套件类型来探测服务器,例如,使用ECDHE与DHE或RSA密钥交换的套件。

手头有两个套件,您需要启动两个连接,首先提供其中一个套件作为您的首选,然后提供另一个:

如果您看到在两个连接上协商了相同的套件,这意味着服务器被配置为主动选择协商的套件。否则,事实并非如此。上例中的服务器是不强制执行首选项的TLS 1.3服务器之一。同一台服务器确实首选TLS 1.2;我们可以看到,它总是选择一个更好的套件,即使我们把它推到列表的末尾:

当涉及到服务器套件偏好测试时,最好避免使用ChaCha20套件。这是因为一些服务器支持另一种类型的偏好,在这种偏好中,它们在安全性方面将AES-GCM和ChaCha20套件视为平等的,并将客户端偏好视为特殊情况。

其想法是,客户端将更喜欢更快的密码套件,通常是用于移动设备的ChaCha20和用于台式机的AES-GCM。也就是说,对于支持这种偏好的服务器,您可能需要测试它是否正常工作。为此,您需要使用三个受支持的密码套件和三个测试。前两个测试的目的是确定服务器在不涉及ChaCha20时选择其最喜欢的套件:

如果你看到服务器在这两种情况下都使用相同的套件进行响应,你可以先使用支持的ChaCha20套件提交另一个测试。如果你看到服务器选择它,你就知道它被配置为支持客户端首选套件:

测试命名组

命名组(named groups)是用于密钥交换的预定义加密参数。在TLS 1.3中,命名组包括椭圆曲线(elliptic curve,EC)和有限域(finite field,DH)参数。TLS 1.2和更早版本通常只使用预定义的椭圆曲线;服务器在每个连接上提供DH参数。在握手过程中,客户端和服务器必须就一个共同命名的组达成一致,密钥交换将在该组上进行,所选组满足所需的安全要求非常重要。

在实践中,很少需要为命名组测试服务器。尽管在各种RFC中有相当多的命名组,但OpenSSL可能是唯一得到广泛支持的主要客户端。从历史上看,你只能使用NIST的P-256和P-384 EC组,因为这些是唯一得到广泛支持的曲线。最近,X25519和X448组被添加为替代品。因为所有这些曲线都很强,所以几乎不需要花时间去思考它们。

您可能会发现自己通常会测试命名组配置,以了解您的web服务器正在做什么。例如,您可能关心X25519,并希望确保它可用且首选。要测试这一点,请使用 s_client 工具和 -curves 开关。例如,以下是如何确定是否支持单个命名组:

成功后,您将在输出中看到指定的组,因为这是为握手选择的组。失败时,您可能看不到输出,这意味着握手失败。或者,无法协商ECDHE套件的服务器可能会退回到DHE套件,如以下输出所示:

如果你需要测试命名组偏好,你需要提供两个或多个命名组,最后一个是你喜欢的。如果你看到它经过协商,这意味着服务器会主动选择它认为最合适的组。使用冒号分隔组,并注意名称区分大小写。

测试DANE

基于DNS的命名实体身份验证(DNS-based Authentication of Named Entities,DANE)是一组标准,使您能够通过DNS配置认可您使用的TLS证书。为了实现这一点,DANE要求DNS本身是安全的,这意味着DNSSEC是必要的。因此,DANE本质上是一种钉扎机制(a mechanism for pinning);只有您批准的证书才会被启用DANE的客户端视为有效。DANE本身没有争议,但它所依赖的DNSSEC是一个非常分裂的话题,世界在爱它和恨它之间分裂。因此,DANE目前没有得到普遍支持。它更常用于保护SMTP服务器;浏览器级别没有支持。

由于DNS配置的传播和缓存方式,支持DANE会增加TLS部署的复杂性。在使用新证书之前,您需要确保您的新DNS配置(认可该证书)已完全传播。因此,您通常会首先发布DNS更改,等待足够的时间来清除缓存,然后才部署证书。

测试本身很简单;在向 s_client 工具提供DANE数据时使用它。这很方便,因为它使您甚至可以在更改DNS之前测试连接。首先,让我们看看DANE配置是什么样子的。

DANE将配置存储在TLSA资源记录中,使用两个前缀标签来指示协议和端口:

此输出包含两个代言(endorsements),每个证书一个。有两个代言并不罕见。例如,您可能有一个使用两个证书的服务(例如,一个使用RSA密钥,另一个使用ECDSA密钥),或者您有一个备份证书,或者您只是在切换证书时处于过渡期。开头的三个数字表示代言通过其公钥(1)和SHA256哈希(1)直接针对证书(3)。其余的数据是哈希值本身。

为了测试,您在使用 -dane_tlsa_domain-dane_tlsa_rrdata 开关提供DANE数据的同时连接到SMTP服务:

如果验证成功,您将在输出中看到类似这样的内容:

如果你想测试验证失败,只需破坏提供的哈希值。结果将类似于以下输出:

为了获得最佳结果,当以这种方式测试DANE时,始终提供所有已知的TLSA记录(每个 -dane_tlsa_rrdata 开关一个)。如果您这样做,无论协商什么证书,同时使用多个证书的服务都将签出。对于TLS 1.2及更早版本,可以通过选择客户端支持的密码套件( -chipher 开关)强制使用特定证书。TLS 1.3套件不同,对于此协议版本,您需要使用 -sigalgs 开关,其值为 ecdsa_secp256r1_sha256rsa_ps_rsae_sha256

测试会话恢复

当与 -reconnect 开关结合使用时,s_client 命令可用于测试会话重用。在此模式下, s_client 将连接到目标服务器六次。它将在第一个连接上创建一个新会话,然后尝试在后续的五个连接中重用同一会话:

前面的命令将产生大量输出,其中大部分您不会关心。关键部分是关于新会话和重用会话的信息。开始时应该只有一次新的会议,如下行所示:

接下来是五次会话重用,用这样的行表示:

大多数时候,您不想查看所有输出,而是希望快速得到答案。你可以使用以下命令行获取它:

以下是该命令的作用:

在连接之间保持会话状态

如果您需要更好地控制恢复,s_client 工具提供了将连接状态持久化到文件的选项。在第一次连接时,使用 -sess_out 开关记录状态:

要查看记录的状态,请使用 sess_id 工具:

最后,要使用相同的会话状态再次连接,请使用 -sess_in 开关:

以这种方式保持连接之间的状态可以让您更好地控制,并使您能够将连接参数从一个连接完全更改为另一个连接。例如,您可以在第一次尝试时连接到一台服务器,然后在第二次尝试时再连接到另一台服务器。当您需要测试web服务器集群上是否正确实现了会话恢复时,这可能会有用。手动控制连接允许您将它们分散到一段时间内,也许可以测试会话超时和票证密钥轮换。

检查OCSP吊销

如果OCSP响应器出现故障,有时很难准确理解原因。从命令行检查证书吊销状态是可能的,但并不十分简单。您需要执行以下步骤:

  1. 获取您要检查吊销的证书。
  2. 获取颁发证书。
  3. 确定OCSP响应程序的URL。
  4. 提交OCSP请求并观察响应。

对于前两个步骤,使用指定的 -showcerts 开关连接到服务器:

输出中的第一个证书将是属于服务器的证书。如果证书链配置正确,则第二个证书将是颁发者的证书。要确认,请检查第一个证书的颁发者是否与第二个证书的主题匹配:

如果第二个证书不正确,请检查链的其余部分;有些服务器没有按照正确的顺序为链提供服务。如果在链中找不到颁发者证书,则必须在其他地方找到它。一种方法是在叶子证书中查找权威信息访问(Authority Information Access)扩展:

如果存在CA颁发者(CA Issuers)信息,则应包含颁发者证书的URL。如果颁发者证书信息不可用,您可以尝试在浏览器中打开该网站,让它重建链,并从其证书查看器下载颁发证书。如果所有这些都失败了,您可以在信任存储中查找证书或访问CA的网站。

如果您已经拥有证书,只需要知道OCSP响应程序的地址,请使用 -ocsp_uri 开关和 x509 命令作为快捷方式:

现在,您可以提交OCSP请求:

你想在回复中寻找两件事。首先,检查响应本身是否有效(上例中的 Response verify OK ),其次,检查响应所说的内容。当您看到状态良好时,这意味着证书尚未被吊销。已吊销证书的状态将被吊销。

您可能会遇到对上一个命令行没有成功响应的OCSP响应程序。在这种情况下,以下建议可能会有所帮助。

考虑到前两点,最终使用的命令如下:

测试OCSP装订

OCSP装订(stapling)是一个可选功能,允许服务器证书附带证明其有效性的OCSP响应。因为OCSP响应是通过已有的连接传递的,所以客户端不必单独获取它。

OCSP装订仅在客户端请求时使用,客户端在握手请求中提交 status_request 扩展。支持OCSP装订的服务器将通过在握手过程中包含OCSP响应来做出响应。

使用s_client工具时,通过 -status 开关请求OCSP装订:

OCSP相关信息将显示在连接输出的最开始。例如,对于不支持装订的服务器,您将在输出顶部附近看到这行:

使用支持装订的服务器,您将在输出中看到整个OCSP响应:

证书状态良好意味着证书尚未被吊销。

检查CRL吊销

使用证书吊销列表(Certificate Revocation List,CRL)检查证书验证比通过OCSP进行检查更复杂。流程如下:

  1. 获取您要检查吊销的证书。
  2. 获取颁发证书。
  3. 下载并验证CRL。
  4. 在CRL中查找证书序列号。

第一步与OCSP检查重叠;要完成这些操作,请按照“检查OCSP撤销”一节中的说明进行操作。

然后从CA获取CRL:

验证CRL是否有效(即由颁发者证书签名):

现在,确定要检查的证书的序列号:

此时,您可以将CRL转换为人类可读的格式并手动检查:

CRL以一些元数据开始,后面是一个已吊销证书的列表,最后以签名结束(我们在上一步中验证了签名)。如果服务器证书的序列号在列表中,则意味着它已被吊销。

如果你不想直观地查找序列号(有些crl可能很长),请使用 grep 查找它,但要注意你的格式与 crl 工具使用的格式匹配。例如:

测试重新协商

在TLS中,重新协商是一个失败的功能,它导致了几个协议弱点,其中一些很容易被利用。TLS 1.3不再支持重新协商,但仍有较旧的服务器支持早期的协议修订版。

s_client 工具有几个功能可以帮助您手动测试重新协商。首先,当您连接时,该工具将报告远程服务器是否支持安全重新协商。这是因为支持安全重新协商的服务器通过在握手阶段交换的特殊TLS扩展来表示对它的支持。当支持可用时,输出可能如下:

如果不支持安全重新协商,输出将略有不同:

即使服务器表示支持安全重新协商,您也可能希望测试它是否也允许客户端启动重新协商。客户端发起的重新协商是一种协议功能,在实践中没有任何作用(因为服务器在需要时总是可以发起重新协商),并使服务器更容易受到拒绝服务攻击。

要启动重新协商,在TLS握手完成后,单独在一行上键入R字符。例如,假设我们正在与HTTP服务器通信,您可以键入请求的第一行,启动重新协商,然后完成请求。以下是与支持客户端发起的重新协商的web服务器交谈时的情况:

当重新协商发生时,服务器将再次将其证书发送给客户端。您可以在输出中看到证书链的验证。之后的下一行继续显示Host请求标头。看到web服务器的响应就是支持重新协商的证明。由于不同版本的SSL/TLS库中解决重新协商问题的方式不同,不支持重新协商的服务器可能会中断连接,或者可能会保持连接打开但拒绝继续讨论(这通常会导致超时)。

不支持重新协商的服务器将断然拒绝连接上的第二次握手:

在撰写本文时,OpenSSL的默认行为是连接到不支持安全重新协商的服务器;它还将接受安全和不安全的重新协商,选择服务器能够做的任何事情。如果重新协商在不支持安全重新协商的服务器上成功,您将知道服务器支持不安全的客户端发起的重新协商。

检测心脏出血

您可以使用OpenSSL或为此目的设计的工具之一手动测试Heartbleed。现在有许多实用程序可用,因为Heartbleed很容易被利用。但是,像往常一样,这些工具的准确性存在问题。有证据表明,一些工具无法检测到易受攻击的服务器。鉴于Heartbleed的严重性,最好手动测试或使用能够让您完全了解该过程的工具进行测试。我将介绍一种仅适用于OpenSSL修改版本的方法。

假设您的版本支持心跳协议(从1.0.1开始,但在1.1.0之前),测试的某些部分不需要对OpenSSL进行修改。例如,要确定远程服务器是否支持心跳协议,请在连接时使用 -tlsextdebug 开关显示服务器扩展:

不返回心跳扩展的服务器不易受Heartbleed攻击。要测试服务器是否响应心跳请求,请使用-msg开关请求显示协议消息,连接到服务器,等待握手完成,然后键入B 并按return

此输出显示完整的心跳请求和响应。两个心跳消息中的第二个和第三个字节指定了有效载荷长度。我们提交了18个字节(12个十六进制)的有效载荷,服务器以相同大小的有效载荷进行了响应。在这两种情况下,还有额外的16个字节的填充。有效载荷中的前两个字节构成序列号,OpenSSL使用该序列号来匹配对请求的响应。剩余的有效载荷字节和填充只是随机数据。

要检测易受攻击的服务器,您必须准备一个特殊版本的OpenSSL,该版本发送的有效载荷长度不正确。易受攻击的服务器采用声明的有效载荷长度,并以如此多的字节进行响应,而不管实际提供的有效载荷的长度如何。

此时,您必须决定是要构建侵入性测试(通过从流程中检索一些数据来利用服务器)还是非侵入性测试。这将取决于你的情况。如果您有测试活动的许可,请使用侵入性测试。有了它,您将能够准确地看到返回的内容,并且不会有错误的空间。例如,某些版本的GnuTLS支持Heartbeat,并会以不正确的有效载荷长度响应请求,但它们实际上不会返回服务器数据。非侵入性检测不能可靠地诊断这种情况。

以下针对OpenSSL 1.0.1h的补丁创建了该测试的非侵入性版本:

要构建非侵入性测试,请将有效载荷长度增加最多16个字节,即填充长度。当易受攻击的服务器响应此类请求时,它将返回填充,但不会返回其他任何内容。要构建侵入性测试,请将有效载荷长度增加32个字节。易受攻击的服务器将以50字节的有效载荷(默认情况下由OpenSSL发送18字节,加上您的32字节)进行响应,并发送16字节的填充。通过以这种方式增加声明的有效负载长度,易受攻击的服务器将返回高达64KB的数据。不易受Heartbleed攻击的服务器将不会响应。

要生成自己的Heartbleed测试工具,请解压缩OpenSSL源代码的新副本,编辑 ssl/t1_lib.c 以进行补丁中的更改,照常编译,但不要安装。生成的openssl二进制文件将放置在 apps/ 子目录中。因为它是静态编译的,所以您可以将其重命名为类似 openssl-heartleed 的名称,并将其移动到永久位置。

以下是一个易受攻击的服务器返回16字节服务器数据(粗体)的输出示例:

如果您想在单个响应中查看检索到的更多数据,请增加有效载荷长度,重新编译,然后再次测试。或者,要检索另一批相同大小的数据,请再次使用 B 命令。

确定Diffie-Hellman参数的强度

从OpenSSL 1.0.2开始,当您连接到服务器时,s_client 命令会打印临时Diffie-Hellman密钥的强度(如果使用的话)。因此,要确定服务器DH参数的强度,您需要做的就是连接到它,同时只提供使用DH密钥交换的套件。例如:

支持导出套件的服务器实际上可能提供更弱的DH参数。要检查这种可能性,请使用旧的OpenSSL连接,同时只提供导出DHE套件:

对于配置良好的服务器,此命令应失败。否则,您可能会看到服务器提供协商不安全的512位DH参数。